package com.lagou.factory;

import com.lagou.annotations.AutoWired;
import com.lagou.annotations.Component;
import com.lagou.utils.StringUtils;
import com.lagou.utils.TransactionManager;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.List;

/**
 * 事务代理工厂，用来获取Cglib动态代理或者是JDK动态代理
 */
@Component("transactionProxyFactory")
public class TransactionProxyFactory {
    private final static Log logger = LogFactory.getLog(TransactionProxyFactory.class);
    @AutoWired
    private TransactionManager transactionManager;

    // 根据是否实现了接口来判断应该用什么方式生成代理
    public Object getProxy(Object target, List<String> transactionalMethods, boolean isInterface) {
        if (isInterface) {
            logger.info("using jdk proxy");
            return getJdkProxy(target, transactionalMethods);
        } else {
            logger.info("using cglib proxy");
            return getCglibProxy(target, transactionalMethods);
        }
    }

    // 获取cglib动态代理
    private Object getCglibProxy(Object object, List<String> transactionalMethods) {
        return Enhancer.create(object.getClass(), new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                return doInvocation(object, method, objects, transactionalMethods);
            }
        });
    }

    // 获取jdk动态代理
    private Object getJdkProxy(Object object, List<String> transactionalMethods) {
        return Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                return doInvocation(object, method, args, transactionalMethods);
            }
        });
    }

    // 执行代理功能
    private Object doInvocation(Object object, Method method, Object[] args, List<String> transactionalMethods) throws Throwable {
        Object result;
        // 拿到被调用方法的索引
        String methodIndex = StringUtils.getMethodIndexByNameAndReturnTypeAndParameterTypes(method);
        // 判断这个方法是否需要执行事务控制
        if (transactionalMethods.contains(methodIndex)) {
            try {
                transactionManager.beginTransaction();
                result = method.invoke(object, args);
                transactionManager.commit();
            } catch (Exception e) {
                e.printStackTrace();
                transactionManager.rollback();
                throw e.getCause();
            }
        } else {
            result = method.invoke(object, args);
        }
        return result;
    }

}
